﻿using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;

namespace WebResourcesManager
{
    /// <summary>
    /// Class that manages action on web resources
    /// in Microsoft Dynamics CRM 2011
    /// </summary>
    internal class WebResourceManager
    {
        #region Variables
        
        /// <summary>
        /// Xrm Organization service
        /// </summary>
        IOrganizationService innerService;

        #endregion Variables

        #region Constructor

        /// <summary>
        /// Initializes a new instance of class WebResourceManager
        /// </summary>
        /// <param name="service">Xrm Organization service</param>
        public WebResourceManager(IOrganizationService service)
        {
            this.innerService = service;
        }

        #endregion Constructor

        #region Methods

        /// <summary>
        /// Retrieves all web resources that are customizable
        /// </summary>
        /// <returns>List of web resources</returns>
        internal EntityCollection RetrieveWebResources()
        {
            try
            {
                QueryByAttribute qba = new QueryByAttribute("webresource");
                qba.ColumnSet = new ColumnSet(true);
                qba.Attributes.AddRange(new string[] { "ishidden", "ismanaged" });
                qba.Values.AddRange(new object[] { false, false });

                return this.innerService.RetrieveMultiple(qba);
            }
            catch (Exception error)
            {
                throw new Exception("Error while retrieving web resources: " + error.Message);
            }
        }

        /// <summary>
        /// Retrieves a specific web resource from its unique identifier
        /// </summary>
        /// <param name="scriptId">Web resource unique identifier</param>
        /// <returns>Web resource</returns>
        internal Entity RetrieveWebResource(Guid webresourceId)
        {
            try
            {
                return this.innerService.Retrieve("webresource", webresourceId, new ColumnSet(true));
            }
            catch (Exception error)
            {
                throw new Exception("Error while retrieving web resource: " + error.Message);
            }
        }

        /// <summary>
        /// Retrieves a specific web resource from its unique name
        /// </summary>
        /// <param name="name">Web resource unique name</param>
        /// <returns>Web resource</returns>
        internal Entity RetrieveWebResource(string name)
        {
            try
            {
                QueryByAttribute qba = new QueryByAttribute("webresource");
                qba.Attributes.Add("name");
                qba.Values.Add(name);
                qba.ColumnSet = new ColumnSet(true);

                EntityCollection collection = this.innerService.RetrieveMultiple(qba);

                if (collection.Entities.Count == 0)
                {
                    return null;
                }
                else if (collection.Entities.Count > 1)
                {
                    throw new Exception(string.Format("there are more than one web resource with name '{0}'", name));
                }
                else
                {
                    return collection[0];
                }
            }
            catch (Exception error)
            {
                throw new Exception("Error while retrieving web resource: " + error.Message);
            }
        }

        /// <summary>
        /// Updates the provided web resource
        /// </summary>
        /// <param name="script">Web resource to update</param>
        internal Guid UpdateWebResource(Entity script)
        {
            try
            {
                if (!script.Contains("webresourceid"))
                {
                    Entity existingEntity = this.RetrieveWebResource(script["name"].ToString());

                    if (existingEntity == null)
                        return this.CreateWebResource(script);
                    else
                    {
                        script.Id = existingEntity.Id;

                        if (!script.Contains("displayname") && existingEntity.Contains("displayname"))
                            script["displayname"] = existingEntity["displayname"];

                        if (!script.Contains("description") && existingEntity.Contains("description"))
                            script["description"] = existingEntity["description"];

                        this.innerService.Update(script);
                        return script.Id;
                    }
                }
                else
                {
                    this.innerService.Update(script);
                    return script.Id;
                }
            }
            catch (Exception error)
            {
                throw new Exception("Error while updating web resource: " + error.Message);
            }
        }

        /// <summary>
        /// Deletes the provided web resource
        /// </summary>
        /// <param name="script">Web resource to delete</param>
        internal void DeleteWebResource(Entity webResource)
        {
            try
            {
                this.innerService.Delete(webResource.LogicalName, webResource.Id);
            }
            catch (Exception error)
            {
                throw new Exception("Error while deleting web resource: " + error.Message);
            }
        }

        /// <summary>
        /// Creates the provided web resource
        /// </summary>
        /// <param name="webResource">Web resource to create</param>
        internal Guid CreateWebResource(Entity webResource)
        {
            try
            {
                return this.innerService.Create(webResource);
            }
            catch (Exception error)
            {
                throw new Exception("Error while creating web resource: " + error.Message);
            }
        }

        internal void PublishWebResources(List<Guid> ids)
        {
            try
            {
                string idsXml = string.Empty;

                foreach (Guid id in ids)
                {
                    idsXml += string.Format("<webresource>{0}</webresource>", id.ToString("B"));
                }

                PublishXmlRequest pxReq1 = new PublishXmlRequest
                {
                    ParameterXml = String.Format("<importexportxml><webresources>{0}</webresources></importexportxml>", idsXml)
                };

                this.innerService.Execute(pxReq1);
            }
            catch (Exception error)
            {
                throw new Exception("Error while publishing web resources: " + error.Message);
            }
        }

        internal static string GetContentFromBase64String(string base64)
        {
            byte[] b = Convert.FromBase64String(base64);
            return System.Text.Encoding.UTF8.GetString(b);
        }

        internal static string GetBase64StringFromString(string content)
        {
            byte[] byt = System.Text.Encoding.UTF8.GetBytes(content);
            return Convert.ToBase64String(byt);
        }

        #endregion
    }
}
